/*
 * TreeModel.cpp  0.4.0 / Dec 03, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
#include "TreeModel.h"

#include "TreeDelegate.h"

// TreeNode :: Public Constructors. --------------------------------------------

/*!
 * \brief Constructs new TreeNode object.
 */
TreeNode::TreeNode()
  : m_node(0), m_parentNode(0), m_childCount(0)
{
  // empty constructor body
}

/*!
 * \brief Constructs new TreeNode object.
 *
 * \param node the TreeNode object
 * \param parent the TreeNode parent
 */
TreeNode::TreeNode(jobject node, TreeNode *parent)
  : m_parentNode(parent), m_childCount(-1)
{
  m_node = JNISwingWrapper::newGlobalRef(node);
  if (!m_node) {
    m_childCount = 0;
  }
}

// TreeNode :: Public Destructor. ----------------------------------------------

/*!
 * \brief Destroys the TreeNode object.
 */
TreeNode::~TreeNode()
{
  if (m_node) {
    JNISwingWrapper::deleteGlobalRef(m_node);
  }
  qDeleteAll(m_childNodes);
}

// TreeNode :: Public Member Functions. ----------------------------------------

/*!
 * \brief Returns the node object.
 *
 * \return node object
 */
jobject TreeNode::node()
{
  return m_node;
}

/*!
 * \brief Returns parent node.
 *
 * \return parent node
 */
TreeNode *TreeNode::parent()
{
  return m_parentNode;
}

/*!
 * \brief Returns node data.
 *
 * \return node data
 */
QVariant TreeNode::data()
{
  if (m_node) {
    jstring result = JNISwingWrapper::toString(m_node);
    if (result) {
      return SwingString(result);
    }
  }
  return QVariant();
}

/*!
 * \brief Returns node's row position.
 *
 * \return node's row position
 */
int TreeNode::row()
{
  if (m_parentNode) {
    return m_parentNode->m_childNodes.indexOf(this);
  }
  return 0;
}

/*!
 * \brief Returns the number of children.
 *
 * \return the number of children
 */
int TreeNode::childCount()
{
  if (m_childCount < 0) {
    expand();
  }
  return m_childCount;
}

/*!
 * \brief Returns \c true if the node is leaf.
 *
 * \return \c true if the node is leaf
 */
bool TreeNode::isLeaf()
{
  return (m_childCount == 0);
}

/*!
 * \brief Returns the child node at the specified position.
 *
 * \param row the node's position
 *
 * \return the child node
 */
TreeNode *TreeNode::child(int row)
{
  return m_childNodes.at(row);
}

/*!
 * \brief Adds child node.
 *
 * \param child the child node
 */
void TreeNode::addChild(TreeNode *child)
{
  m_childNodes.append(child);
  m_childCount++;
}

// TreeNode :: Private Member Function. ----------------------------------------

void TreeNode::expand()
{
  jint childCount;
  if (JNISwingWrapper::callIntMethod(childCount, m_node, sm_getChildCountMID)) {
    m_childCount = childCount;
    for (int i = 0; i < childCount; i++) {
      jobject childNode;
      if (JNISwingWrapper::callObjectMethod(childNode, m_node,
                                            sm_getChildAtMID, i)) {
        m_childNodes.append(new TreeNode(childNode, this));
      } else{
        m_childCount = 0;
        break;
      }
    }
  } else {
    m_childCount = 0;
  }
}

// TreeNode :: Public Static Member Function. ----------------------------------

/*!
 * \brief Initializes the TreeNode class.
 *
 * \param env the JNIEnv pointer
 * \param cls the ThreeNode class
 *
 * \exception FatalError if failed
 */
void TreeNode::initialize(JNIEnv *env, jclass cls)
{
  Q_ASSERT(env);
  Q_ASSERT(cls);
  Q_ASSERT(!sm_getChildCountMID);
  if (!INIT_MID(getChildCount, cls, "()I") ||
      !INIT_MID(getChildAt, cls, "(I)Lcom/cute4j/swing/tree/TreeNode;")) {
    FATAL_LOGGED_ERROR("TreeNode initialization failed");
  }
}

// TreeNode :: Private Static Data Members. ------------------------------------

jmethodID TreeNode::sm_getChildCountMID = 0;
jmethodID TreeNode::sm_getChildAtMID = 0;

// TreeNode :: Public Constructor. ---------------------------------------------

/*!
 * \brief Constructs new TreeModel object.
 *
 * \param treeDelegate the TreeDelegate pointer
 */
TreeModel::TreeModel(TreeDelegate *treeDelegate)
  : QAbstractItemModel(), m_treeDelegate(treeDelegate), m_rootNode(0),
    m_modelRootNode(0)
{
  Q_ASSERT(treeDelegate);
}

// TreeNode :: Public Destructor. ----------------------------------------------

/*!
 * \brief Destroys the TreeModel object.
 */
TreeModel::~TreeModel()
{
  if (m_rootNode) {
    delete m_rootNode;
  }
}

// TreeNode :: Public Member Function. -----------------------------------------

/*!
 * \brief Returns root node.
 *
 * \return root node
 */
TreeNode *TreeModel::getRootNode()
{
  if (!m_rootNode) {
    m_rootNode = new TreeNode();
    m_modelRootNode = new TreeNode(m_treeDelegate->getRootNode(), m_rootNode);
    m_rootNode->addChild(m_modelRootNode);
  }
  return m_rootNode;
}

// TreeNode :: Overridden Member Functions. ------------------------------------

/*!
 * \inheritDoc
 */
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
  if (!index.isValid()) {
    return Qt::ItemIsEnabled;
  }
  return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

/*!
 * \inheritDoc
 */
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
  if (index.isValid()) {
    TreeNode *node = static_cast<TreeNode *>(index.internalPointer());
    if (node) {
      TreeNode *parentNode = node->parent();
      if (parentNode) {
        if (parentNode != m_rootNode) {
          return createIndex(parentNode->row(), 0, parentNode);
        }
      }
    }
  }
  return QModelIndex();
}

/*!
 * \inheritDoc
 */
int TreeModel::rowCount(const QModelIndex &index) const
{
  if (index.column() > 0) {
    return 0;
  }
  TreeNode *node;
  if (index.isValid()) {
    node = static_cast<TreeNode *>(index.internalPointer());
  } else {
    const_cast<TreeModel *>(this)->getRootNode();
    node = m_rootNode;
  }
  return node->childCount();
}

/*!
 * \inheritDoc
 */
int TreeModel::columnCount(const QModelIndex &) const
{
  return 1;
}

/*!
 * \inheritDoc
 */
QModelIndex TreeModel::index(int row, int column,
                             const QModelIndex &parent) const
{
  if ((column == 0) && (row >= 0) && hasIndex(row, column, parent)) {
    TreeNode *node;
    if (parent.isValid()) {
      node = static_cast<TreeNode *>(parent.internalPointer());
    } else {
      node = m_rootNode;
    }
    TreeNode *childNode = node->child(row);
    if (childNode) {
      return createIndex(row, 0, childNode);
    }
  }
  return QModelIndex();
}

/*!
 * \inheritDoc
 */
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
  if (index.isValid()) {
    TreeNode *node = static_cast<TreeNode *>(index.internalPointer());
    if (node) {
      switch (role) {
        case Qt::DisplayRole:
          return node->data();
        case Qt::DecorationRole:
          QIcon *nodeIcon = m_treeDelegate->getNodeIcon(node->node());
          if (nodeIcon) {
            return *nodeIcon;
          } else if (node->isLeaf()) {
            return QApplication::style()->standardIcon(QStyle::SP_FileIcon);
          } else {
            return QApplication::style()->standardIcon(QStyle::SP_DirIcon);
          }
      }
    }
  }
  return QVariant();
}

/*!
 * \inheritDoc
 */
QVariant TreeModel::headerData(int, Qt::Orientation, int) const
{
  return QVariant();
}
